
/**
*
* @author zzhijie
* @date 2020-08-19
*/

<template>
  <div class="drag-wrapper" ref="wrapper" :class="moveFlag?'placeholder':''"
       :style="getElSize">
    <div
      ref="dragItem"
      class="drag-item"
      :class="[moveFlag?'moving':'']"
      :style="{...getElPosition,...getElSize}"
      @mousedown="handleStart"
      @mouseleave="handleLeave"
    >
      <slot></slot>
    </div>
  </div>
</template>

<script>

export default {
  name: "dragItem",
  props: {
    // 当前组件的索引
    index: {
      type: Number,
      required: true
    },
    // 需要排序的列表
    sortList: {
      type: Array,
      required: true
    },
    // 排序方向：0上下，1左右
    sortDirection: {
      type: Number,
      default: 0
    },
    dragDelay: {
      type: Number,
      default: 100
    }
  },
  data() {
    return {
      moveFlag: false,
      top: 0,
      left: 0,
      rect: {},
      targetIndex: -1,
    }
  },
  computed: {
    dropBox() {
      return this.$refs.wrapper.parentElement;
    },
    getElSize() {
      let {width, height} = this.rect;
      return {
        width: width ? width + 'px' : null,
        height: height ? height + 'px' : null
      }
    },
    getElPosition() {
      return {
        top: this.top ? this.top + 'px' : null,
        left: this.left ? this.left + 'px' : null
      }
    }
  },
  watch: {},
  methods: {
    setPosition(e) {
      let {pageX, pageY} = e;
      this.top = pageY - this.y;
      this.left = pageX - this.x;
    },
    handleStart(e) {
      // 鼠标按下再添加mousemove和mouseup事件，防止不必要的性能损耗
      addEventListener("mousemove", this.handleMove);
      addEventListener("mouseup", this.handleUp);
      clearTimeout(this.timer);
      this.timer = setTimeout(() => {
        this.rect = this.$refs.wrapper.getBoundingClientRect();
        this.x = e.pageX - this.rect.left;
        this.y = e.pageY - this.rect.top;
        this.setPosition(e);
        this.dropBox.appendChild(this.$refs.dragItem);
        this.moveFlag = true;
      }, this.dragDelay);
    },
    getActiveItem(e) {
      if (this.animateFlag) return;
      let dragList = this.dropBox.querySelectorAll(".drag-wrapper");
      Array.prototype.find.call(dragList, (el, index) => {
        if (this.index !== index) {
          let rect = el.getBoundingClientRect();
          if (this.isOverlap(e, rect)) {
            this.targetIndex = index;
            this.toggleComponent(e, rect);
            return true;
          } else {
            this.targetIndex = -1;
          }
        }
      })
    },
    isOverlap(e, rect) {
      let {pageX, pageY} = e;
      let {top, left, width, height} = rect;
      return (pageX > left && pageX < left + width) && (pageY > top && pageY < top + height);
    },
    isNeedToggle(e, rect) {
      if (this.sortDirection === 0) {
        return this.isNeedVerticalToggle(e, rect);
      } else {
        return this.isNeedHorizontalToggle(e, rect);
      }
    },
    isNeedVerticalToggle(e, rect) {
      let {pageY} = e;
      let {top, height} = rect;
      let moreThenHalf = pageY > top + (height >> 1);
      let toggleToFront = moreThenHalf && this.index < this.targetIndex;
      let toggleToBack = !moreThenHalf && this.index > this.targetIndex;
      return toggleToFront || toggleToBack;
    },
    isNeedHorizontalToggle(e, rect) {
      let {pageX} = e;
      let {left, width} = rect;
      let moreThenHalf = pageX > left + (width >> 1);
      let toggleToFront = moreThenHalf && this.index < this.targetIndex;
      let toggleToBack = !moreThenHalf && this.index > this.targetIndex;
      return toggleToFront || toggleToBack;
    },
    handleMove(e) {
      if (!this.moveFlag) return;
      this.setPosition(e);
      this.getActiveItem(e);
    },
    toggleComponent(e, rect) {
      if (this.targetIndex > -1) {
        let isNeedToggle = this.isNeedToggle(e, rect);
        if (isNeedToggle) {
          this.animateFlag = true;
          let [temp] = this.sortList.splice(this.index,1);
          this.sortList.splice(this.targetIndex, 0, temp);
          setTimeout(() => {
            this.animateFlag = false;
          }, 300);
        }
      }
    },
    handleUp() {
      removeEventListener("mousemove", this.handleMove);
      removeEventListener("mouseup", this.handleUp);
      this.moveFlag = false;
      this.rect = {};
      this.top = null;
      this.left = null;
      clearTimeout(this.timer);
      let hasChild = this.$refs.wrapper.hasChildNodes();
      if (!hasChild) {
        this.$refs.wrapper.appendChild(this.$refs.dragItem);
      }
    },
    handleLeave() {
      clearTimeout(this.timer);
    }
  },
  mounted() {
  },
  beforeDestroy() {
    removeEventListener("mousemove", this.handleMove);
    removeEventListener("mouseup", this.handleUp);
  }
}
</script>

<style lang="scss" scoped>
.drag-wrapper {
  position: relative;
  user-select: none;
  //z-index: 1;

  &.placeholder {
    //border: 1px dashed #ddd;
    background: rgba(0, 0, 0, .03);
  }
}

.drag-item {
  //position: relative;
  cursor: move;
}

.moving {
  position: fixed !important;
  z-index: 9999;
  //background: #fff;
  opacity: .6;
}
</style>
